﻿using Sidvall.UI.ViewModels;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Input;

namespace Sidvall.UI.Wpf.Controls
{
    public partial class TreeControl : UserControl
    {
        #region Public Members

        #region ViewModel

        public TreeControlViewModel ViewModel
        {
            get
            {
                return (TreeControlViewModel)this.DataContext;
            }
            set
            {
                this.DataContext = value;
            }
        }

        #endregion
        #region PopupHeight

        public static readonly System.Windows.DependencyProperty PopupHeightProperty = System.Windows.DependencyProperty.Register(nameof(PopupHeight), typeof(double), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(100d, System.Windows.FrameworkPropertyMetadataOptions.None, null, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public double PopupHeight
        {
            get
            {
                return (double)GetValue(PopupHeightProperty);
            }
            set
            {
                SetValue(PopupHeightProperty, value);
            }
        }

        #endregion
        #region PopupDialogHeader

        public static readonly System.Windows.DependencyProperty PopupDialogHeaderProperty = System.Windows.DependencyProperty.Register(nameof(PopupDialogHeader), typeof(object), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(null, System.Windows.FrameworkPropertyMetadataOptions.None, null, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public object PopupDialogHeader
        {
            get
            {
                return GetValue(PopupDialogHeaderProperty);
            }
            set
            {
                SetValue(PopupDialogHeaderProperty, value);
            }
        }

        #endregion
        #region PopupButtonWidth

        public static readonly System.Windows.DependencyProperty PopupButtonWidthProperty = System.Windows.DependencyProperty.Register(nameof(PopupButtonWidth), typeof(double), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(18d, System.Windows.FrameworkPropertyMetadataOptions.None, OnPopupButtonWidthPropertyChanged, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public double PopupButtonWidth
        {
            get
            {
                return (double)GetValue(PopupButtonWidthProperty);
            }
            set
            {
                SetValue(PopupButtonWidthProperty, value);
            }
        }

        #endregion
        #region PopupButtonImageMargin

        public static readonly System.Windows.DependencyProperty PopupButtonImageMarginProperty = System.Windows.DependencyProperty.Register(nameof(PopupButtonImageMargin), typeof(System.Windows.Thickness), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(new System.Windows.Thickness(0), System.Windows.FrameworkPropertyMetadataOptions.None, null, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public System.Windows.Thickness PopupButtonImageMargin
        {
            get
            {
                return (System.Windows.Thickness)GetValue(PopupButtonImageMarginProperty);
            }
            set
            {
                SetValue(PopupButtonImageMarginProperty, value);
            }
        }

        #endregion
        #region SelectedValue

        public static readonly System.Windows.DependencyProperty SelectedValueProperty = System.Windows.DependencyProperty.Register(nameof(SelectedValue), typeof(object), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(null, System.Windows.FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedValuePropertyChanged, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public object SelectedValue
        {
            get
            {
                return GetValue(SelectedValueProperty);
            }
            set
            {
                SetValue(SelectedValueProperty, value);
            }
        }

        #endregion
        #region SelectedValuePath

        public static readonly System.Windows.DependencyProperty SelectedValuePathProperty = System.Windows.DependencyProperty.Register(nameof(SelectedValuePath), typeof(string), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(null, System.Windows.FrameworkPropertyMetadataOptions.None, OnSelectedValuePathPropertyChanged, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public string SelectedValuePath
        {
            get
            {
                return GetValue(SelectedValuePathProperty) as string;
            }
            set
            {
                SetValue(SelectedValuePathProperty, value);
            }
        }

        #endregion
        #region ShowClearButton

        public static readonly System.Windows.DependencyProperty ShowClearButtonProperty = System.Windows.DependencyProperty.Register(nameof(ShowClearButton), typeof(bool), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(true, System.Windows.FrameworkPropertyMetadataOptions.None, OnUsePopupDialogPropertyChanged, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public bool ShowClearButton
        {
            get
            {
                return (bool)GetValue(UsePopupDialogProperty);
            }
            set
            {
                SetValue(UsePopupDialogProperty, value);
            }
        }

        #endregion
        #region TreeViewBackground

        public static readonly System.Windows.DependencyProperty TreeViewBackgroundProperty = System.Windows.DependencyProperty.Register(nameof(TreeViewBackground), typeof(System.Windows.Media.Brush), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(System.Windows.Media.Brushes.White, System.Windows.FrameworkPropertyMetadataOptions.None, null, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public System.Windows.Media.Brush TreeViewBackground
        {
            get
            {
                return (System.Windows.Media.Brush)GetValue(TreeViewBackgroundProperty);
            }
            set
            {
                SetValue(TreeViewBackgroundProperty, value);
            }
        }

        #endregion
        #region UsePopupDialog

        public static readonly System.Windows.DependencyProperty UsePopupDialogProperty = System.Windows.DependencyProperty.Register(nameof(UsePopupDialog), typeof(bool), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(false, System.Windows.FrameworkPropertyMetadataOptions.None, OnUsePopupDialogPropertyChanged, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public bool UsePopupDialog
        {
            get
            {
                return (bool)GetValue(UsePopupDialogProperty);
            }
            set
            {
                SetValue(UsePopupDialogProperty, value);
            }
        }

        #endregion
        #region ValueTextBoxBackground

        public static readonly System.Windows.DependencyProperty ValueTextBoxBackgroundProperty = System.Windows.DependencyProperty.Register(nameof(ValueTextBoxBackground), typeof(System.Windows.Media.Brush), typeof(TreeControl),
            new System.Windows.FrameworkPropertyMetadata(System.Windows.Media.Brushes.White, System.Windows.FrameworkPropertyMetadataOptions.None, null, null, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
        public System.Windows.Media.Brush ValueTextBoxBackground
        {
            get
            {
                return (System.Windows.Media.Brush)GetValue(ValueTextBoxBackgroundProperty);
            }
            set
            {
                SetValue(ValueTextBoxBackgroundProperty, value);
            }
        }

        #endregion

        #endregion
        #region Private Members

        private List<string> _SelectedItemIds;
        private bool EventsEnabled = true;
        #region CloseOption

        private enum CloseOption
        {
            ApplySelectItems,
            ClearSelectItems,
            ResetSelectItems,
        }

        #endregion

        #region OnDataContextChanged

        private void OnDataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
        {
            var oldItem = e.OldValue as TreeControlViewModel;
            var newItem = e.NewValue as TreeControlViewModel;
            if (oldItem != null)
            {
                oldItem.PropertyChanged -= OnTreeControlViewModelPropertyChanged;
            }
            if (newItem != null)
            {
                newItem.SelectedValuePath = this.SelectedValuePath;
                newItem.PropertyChanged += OnTreeControlViewModelPropertyChanged;
            }
        }

        #endregion
        #region OnSelectedValuePropertyChanged

        private static void OnSelectedValuePropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
        {
            var parent = d as TreeControl;
            if (parent?.ViewModel == null)
                return;
            if (!parent.EventsEnabled)
                return;
            parent.EventsEnabled = false;
            try
            {
                parent.ViewModel.SelectedValue = parent.SelectedValue;
            }
            finally
            {
                parent.EventsEnabled = true;
            }
        }

        #endregion
        #region OnSelectedValuePathPropertyChanged

        private static void OnSelectedValuePathPropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
        {
            var parent = d as TreeControl;
            if (parent?.ViewModel == null)
                return;
            parent.ViewModel.SelectedValuePath = parent.SelectedValuePath;
        }

        #endregion
        #region OnTreeControlViewModelPropertyChanged

        private void OnTreeControlViewModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (!this.EventsEnabled)
                return;
            if (e.PropertyName == nameof(TreeControlViewModel.SelectedValue))
            {
                this.EventsEnabled = false;
                try
                {
                    this.SelectedValue = this.ViewModel.SelectedValue;
                }
                finally
                {
                    this.EventsEnabled = true;
                }
            }
        }

        #endregion
        #region OnUsePopupDialogPropertyChanged

        private static void OnUsePopupDialogPropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
        {
            InitializeTreeControl(d as TreeControl);
        }

        #endregion
        #region OnPopupButtonWidthPropertyChanged

        private static void OnPopupButtonWidthPropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
        {
            InitializeTreeControl(d as TreeControl);
        }

        #endregion

        #region InitializePopupButton

        private void InitializePopupButton(bool isChecked)
        {
            _EventsEnabled = false;
            try
            {
                this.PopupButton.IsChecked = isChecked;
            }
            finally
            {
                _EventsEnabled = true;
            }
        }

        #endregion
        #region InitializeTreeControl

        private static void InitializeTreeControl(TreeControl treeControl)
        {
            if (treeControl == null)
                return;
            if (treeControl.UsePopupDialog)
            {
                treeControl.ParentGrid.Children.Remove(treeControl.TreeView);
                treeControl.TreeView.Visibility = System.Windows.Visibility.Visible;
                treeControl.TreePopupDialogContent.Content = treeControl.TreeView;
            }
            else
            {
                treeControl.ParentGrid.Children.Remove(treeControl.TreeView);
                treeControl.TreeView.Visibility = System.Windows.Visibility.Visible;
                treeControl.TreePopup.Child = treeControl.TreeView;
            }
            if (treeControl.PopupButtonWidth < 15)
                treeControl.PopupButtonImageMargin = new System.Windows.Thickness(0);
            else if (treeControl.PopupButtonWidth < 18)
                treeControl.PopupButtonImageMargin = new System.Windows.Thickness(1);
            else if (treeControl.PopupButtonWidth < 21)
                treeControl.PopupButtonImageMargin = new System.Windows.Thickness(2);
            else if (treeControl.PopupButtonWidth < 24)
                treeControl.PopupButtonImageMargin = new System.Windows.Thickness(3);
            else if (treeControl.PopupButtonWidth < 27)
                treeControl.PopupButtonImageMargin = new System.Windows.Thickness(4);
            else if (treeControl.PopupButtonWidth < 30)
                treeControl.PopupButtonImageMargin = new System.Windows.Thickness(5);
            else
                treeControl.PopupButtonImageMargin = new System.Windows.Thickness(6);
        }

        #endregion
        #region ClosePopup

        private void ClosePopup(CloseOption option)
        {
            switch (option)
            {
                case CloseOption.ClearSelectItems:
                    this.ViewModel.ClearSelectItems();
                    break;
                case CloseOption.ResetSelectItems:
                    this.ViewModel.SelectedItemIds = _SelectedItemIds;
                    this.ViewModel.ApplySelectItems();
                    break;
                default:
                    this.ViewModel.ApplySelectItems();
                    break;
            }
        }

        #endregion
        #region Popup

        private void HidePopup(CloseOption option)
        {
            ClosePopup(option);
            this.TreePopup.IsOpen = false;
        }
        private void ShowPopup()
        {
            if (this.ViewModel.AllowMultipleSelections)
            {
                this.TreeView.CheckBoxSettings.CheckBoxVisibility = System.Windows.Visibility.Visible;
            }
            else
            {
                this.TreeView.CheckBoxSettings.CheckBoxVisibility = System.Windows.Visibility.Collapsed;
            }
            this.TreePopup.PlacementTarget = this.ValueTextBox;
            this.TreePopup.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint;
            this.TreePopup.VerticalOffset = this.ValueTextBox.ActualHeight;
            this.TreePopup.StaysOpen = false;
            this.TreePopup.Height = this.PopupHeight;
            this.TreePopup.Width = this.ActualWidth;
            this.TreePopup.IsOpen = true;
            if (!this.ViewModel.AllowMultipleSelections)
            {
                this.TreeView.ScrollNodeIntoView(this.TreeView.ActiveNode);
            }
        }

        #endregion
        #region PopupDialog

        private void HidePopupDialog(CloseOption option)
        {
            ClosePopup(option);
            this.TreePopupDialog.IsOpen = false;
        }
        private void ShowPopupDialog()
        {
            if (this.ViewModel.AllowMultipleSelections)
            {
                this.TreeView.CheckBoxSettings.CheckBoxVisibility = System.Windows.Visibility.Visible;
            }
            else
            {
                this.TreeView.CheckBoxSettings.CheckBoxVisibility = System.Windows.Visibility.Collapsed;
            }
            this.TreePopupDialog.PlacementTarget = this.ValueTextBox;
            this.TreePopupDialog.Placement = System.Windows.Controls.Primitives.PlacementMode.RelativePoint;
            this.TreePopupDialog.VerticalOffset = this.ValueTextBox.ActualHeight;
            this.TreePopupDialog.StaysOpen = false;
            this.TreePopupDialog.Height = this.PopupHeight;
            this.TreePopupDialog.Width = this.ActualWidth;
            this.TreePopupDialog.IsOpen = true;
            if (!this.ViewModel.AllowMultipleSelections)
            {
                this.TreeView.ScrollNodeIntoView(this.TreeView.ActiveNode);
            }
        }

        #endregion

        #endregion
        #region Events

        private bool _EventsEnabled = true;

        #region ClearSelection_Click

        private void ClearSelection_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            if (this.UsePopupDialog)
            {
                InitializePopupButton(false);
                HidePopupDialog(CloseOption.ClearSelectItems);
            }
            else
            {
                HidePopup(CloseOption.ClearSelectItems);
            }
        }

        #endregion
        #region TreeView_ActiveNodeChanged

        private void TreeView_ActiveNodeChanged(object sender, Infragistics.Controls.Menus.ActiveNodeChangedEventArgs e)
        {
            if (!this.ViewModel.AllowMultipleSelections)
            {
                if (this.ViewModel.ApplySelectItems())
                {
                    this.TreePopup.IsOpen = false;
                }
                else
                {
                    var p = e.NewActiveTreeNode?.Control?.Parent as Infragistics.Controls.Menus.Primitives.NodesPanel;
                    if (p != null)
                    {
                        foreach (var c in p.Children)
                        {
                            var d = c as Infragistics.Controls.Menus.XamDataTreeNodeControl;
                            if (d?.Node == null)
                                continue;
                            if (d.Node == e.NewActiveTreeNode)
                                d.Node.IsExpanded = true;
                            else
                                d.Node.IsExpanded = false;
                        }
                    }
                }
            }
        }

        #endregion
        #region TreeView_NodeExpansionChanged

        private async void TreeView_NodeExpansionChanged(object sender, Infragistics.Controls.Menus.NodeExpansionChangedEventArgs e)
        {
            if (e.Node.IsExpanded)
            {
                var treeItem = e.Node.Data as Sidvall.UI.Entities.TreeItem;
                await this.ViewModel.LoadChildItemsAsync(treeItem);
            }
        }

        #endregion
        #region TreePopup_Closed

        private void TreePopup_Closed(object sender, System.EventArgs e)
        {
            if (this.PopupButton != Mouse.DirectlyOver)
                InitializePopupButton(false);
            HidePopup(CloseOption.ApplySelectItems);
        }

        #endregion
        #region TreePopupDialog_Closed

        private void TreePopupDialog_Closed(object sender, System.EventArgs e)
        {
            if (this.PopupButton != Mouse.DirectlyOver)
                InitializePopupButton(false);
            HidePopupDialog(CloseOption.ApplySelectItems);
        }

        #endregion
        #region ValueTextBox_PreviewKeyDown

        private void ValueTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (Keyboard.Modifiers.HasFlag(ModifierKeys.Control))
            {
                switch (e.Key)
                {
                    case Key.A:
                    case Key.C:
                        return;
                    default:
                        break;
                }
            }
            if (Keyboard.Modifiers == ModifierKeys.None)
            {
                if (e.Key == Key.Tab)
                    return;
            }
            e.Handled = true;
        }

        #endregion

        #region ToggleButton_Checked

        private void ToggleButton_Checked(object sender, System.Windows.RoutedEventArgs e)
        {
            if (this.ViewModel.SelectedItemIds != null)
                _SelectedItemIds = new List<string>(this.ViewModel.SelectedItemIds);
            else
                _SelectedItemIds = new List<string>();
            if (this.UsePopupDialog)
            {
                ShowPopupDialog();
            }
            else
            {
                ShowPopup();
            }
        }

        #endregion
        #region ToggleButton_Unchecked

        private void ToggleButton_Unchecked(object sender, System.Windows.RoutedEventArgs e)
        {
            if (!_EventsEnabled)
                return;
            if (this.UsePopupDialog)
            {
                HidePopupDialog(CloseOption.ApplySelectItems);
            }
            else
            {
                HidePopup(CloseOption.ApplySelectItems);
            }
        }

        #endregion

        #region CancelButton_Click

        private void CancelButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            InitializePopupButton(false);
            HidePopupDialog(CloseOption.ResetSelectItems);
        }

        #endregion
        #region OKButton_Click

        private void OKButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            InitializePopupButton(false);
            HidePopupDialog(CloseOption.ApplySelectItems);
        }

        #endregion

        #endregion
        #region Constructors

        public TreeControl()
        {
            InitializeComponent();

            this.DataContextChanged += OnDataContextChanged;
            InitializeTreeControl(this);
        }

        #endregion
    }
}
